//*************************************************************************************
//* 作    者： 
//* 创建时间： 2021-09-06 09:44:49
//* 描    述： 

//* ************************************************************************************
using UnityEngine;
using System.Collections.Generic;
using HighlightPlus;

/// <summary>
/// 表现管理器
/// <summary>
public class PerformAgent : AgentContext<PerformAgent>, UFrame.ILateUpdate
{
    protected Material m_uiGrayMaterial;
    protected Material m_transparentMat;
    protected Material m_worldGrayMaterial;
    protected Material m_supperMat;
    protected OutlineCtrl m_outlineCtrl;
    protected DiamondMaterialChangeCtrl m_diamondMatChangeCtrl;
    protected Color m_yellowColor = new Color(1, 0.5f, 1);
    protected Dictionary<int, FlashOnData> m_flashOnDic = new Dictionary<int, FlashOnData>();
    public bool Runing => m_flashOnDic.Count > 0;
    public float Interval => 0.1f;
    protected HighlightProfile m_selectProfile;
    //protected HighlightProfile m_simpleProfile;
    protected Dictionary<int, GameObject> m_highlightRenderMap = new Dictionary<int, GameObject>();
    protected HashSet<int> m_hidingRenders = new HashSet<int>();
    protected GameObject m_forcusEffectInstance;
    protected override void OnCreate()
    {
        base.OnCreate();
        m_uiGrayMaterial = Resources.Load<Material>("ui_gray");
        m_worldGrayMaterial = Resources.Load<Material>("Materials/world_gray");
        m_transparentMat = Resources.Load<Material>("Materials/diamond_transparent");
        m_selectProfile = Resources.Load<HighlightProfile>("SelectHighlight");
        //m_simpleProfile = Resources.Load<HighlightProfile>("SimpleHighlight");
        m_outlineCtrl = new OutlineCtrl();
        m_diamondMatChangeCtrl = new DiamondMaterialChangeCtrl();
    }

    public void SetGray(UnityEngine.UI.Graphic graphic, bool gray)
    {
        if (!graphic)
            return;

        if (gray)
        {
            graphic.material = m_uiGrayMaterial;
        }
        else
        {
            graphic.material = null;
        }
    }

    protected void HighLightWith(GameObject target, bool on, HighlightProfile profile)
    {
        if (on)
        {
            var renders = target.GetComponentsInChildren<Renderer>();
            for (int i = 0; i < renders?.Length; i++)
            {
                var renderIndex = renders[i].GetInstanceID();
                if (!m_hidingRenders.Contains(renderIndex) && !renders[i].enabled)
                {
                    m_hidingRenders.Add(renderIndex);
                }
                renders[i].enabled = true;
            }
            var highLightEffect = target.MustComponent<HighlightEffect>();
            highLightEffect.ProfileLoad(profile);
            highLightEffect.SetHighlighted(true);
            highLightEffect.enabled = true;
            m_highlightRenderMap[target.GetInstanceID()] = target;
        }
        else
        {
            var highLightEffect = target.GetComponent<HighlightEffect>();
            if (highLightEffect)
            {
                m_highlightRenderMap.Remove(target.GetInstanceID());
                highLightEffect.SetHighlighted(false);
                highLightEffect.enabled = false;
            }

            var renders = target.GetComponentsInChildren<Renderer>();
            for (int i = 0; i < renders?.Length; i++)
            {
                var renderIndex = renders[i].GetInstanceID();
                if (m_hidingRenders.Contains(renderIndex))
                {
                    renders[i].enabled = false;
                }
            }
        }
    }

    internal void ChangeGrayStarMat(Renderer r)
    {
        throw new System.NotImplementedException();
    }

    internal void ChangeGoldStarMat(Renderer r)
    {
        throw new System.NotImplementedException();
    }

    internal void ChangeSupperStarMat(Renderer r)
    {
        throw new System.NotImplementedException();
    }

    public void HighLight(GameObject target, bool on = true)
    {
        HighLightWith(target, on, m_selectProfile);
    }

    public void ClearAllHighlighted()
    {
        foreach (var pair in m_highlightRenderMap)
        {
            if (pair.Value)
            {
                var highLightEffect = pair.Value.GetComponent<HighlightEffect>();
                if (highLightEffect)
                    GameObject.Destroy(highLightEffect);
            }
        }
        m_highlightRenderMap.Clear();
        m_hidingRenders.Clear();
    }

    public void Forcus(GameObject target)
    {
        var boxCollider = target.GetComponentInChildren<BoxCollider>();
        if (boxCollider)
        {
            if (!m_forcusEffectInstance)
                m_forcusEffectInstance = SourceAgent.Instance.LoadGameObjectInstence("FocusEfx");
            var size = boxCollider.size;
            var center = boxCollider.transform.TransformPoint(boxCollider.center);
            m_forcusEffectInstance.transform.SetParent(boxCollider.transform);
            m_forcusEffectInstance.transform.position = center;
            m_forcusEffectInstance.transform.localScale = size;
            m_forcusEffectInstance.transform.localRotation = boxCollider.transform.localRotation;
        }
        else
        {
            Debug.LogError("focus must have collider!");
        }
    }


    public void ShowGridFrame(GameObject target)
    {
        var boxCollider = target.GetComponentInChildren<BoxCollider>();
        if (boxCollider)
        {
            var cubeInstance = SourceAgent.Instance.LoadSource<GameObject>("CubeFrame", UFrame.Source.SourceLoadOption.MakeCopy);
            var size = boxCollider.size;
            size.x *= boxCollider.transform.lossyScale.x;
            size.y *= boxCollider.transform.lossyScale.y;
            size.z *= boxCollider.transform.lossyScale.z;
            var center = boxCollider.transform.TransformPoint(boxCollider.center);
            cubeInstance.transform.position = center;
            cubeInstance.transform.localScale = size;
            cubeInstance.transform.SetParent(target.transform.parent);
            cubeInstance.transform.localRotation = boxCollider.transform.localRotation;
        }
    }

    public void ClearForcus()
    {
        if (m_forcusEffectInstance)
            GameObject.Destroy(m_forcusEffectInstance);
    }

    public void Outlight(GameObject target, bool on)
    {
        m_outlineCtrl.ShowOutline(target, on, Color.green, 3, UFrame.Preform.OutlineBehaviour.Mode.OutlineAll);
    }

    public void GLBox(GameObject target, bool on)
    {
        var boxView = target.GetComponent<BoxColliderViewBehaviour>();
        if (on)
        {
            if (!boxView)
                boxView = target.AddComponent<BoxColliderViewBehaviour>();
            boxView.enabled = true;
            boxView.Show();
        }
        else
        {
            if (boxView)
            {
                boxView.Hide();
            }
        }
    }

    public void OutlightYellow(GameObject target, bool on)
    {
        m_outlineCtrl.ShowOutline(target, on, m_yellowColor, 3, UFrame.Preform.OutlineBehaviour.Mode.OutlineAll);
    }

    public void FlashOn(GameObject target, Color start, Color end)
    {
        if (target)
        {
            if (!m_flashOnDic.TryGetValue(target.GetInstanceID(), out var tuple))
            {
                tuple = m_flashOnDic[target.GetInstanceID()] = new FlashOnData(target, start, end);
            }
            else
            {
                tuple.startColor = start;
                tuple.endColor = end;
            }
        }
    }

    public void FlashOff(GameObject target)
    {
        if (target)
            m_flashOnDic.Remove(target.GetInstanceID());
    }

    public void TransparentDiamond(GameObject target, Material mat = default, bool transparent = true, bool needRecover = false)
    {
        if (transparent)
        {
            m_diamondMatChangeCtrl.ChangeMaterial(target, mat ?? m_transparentMat, needRecover);
        }
        else
        {
            m_diamondMatChangeCtrl.RecoverMaterial(target);
        }
    }

    public void GrayDiamond(GameObject target, Material mat = default, bool gray = true, bool needRecover = false)
    {
        if (gray)
        {
            m_diamondMatChangeCtrl.ChangeMaterial(target, mat ?? m_worldGrayMaterial, needRecover);
        }
        else
        {
            m_diamondMatChangeCtrl.RecoverMaterial(target);
        }
    }

    public void ChangeSupperMat(Renderer target)
    {
        if (!m_supperMat)
            m_supperMat = SourceAgent.Instance.LoadSource<Material>("Materials/super_star");
        target.material = m_supperMat;
        target.material.SetColor("_BaseColor", new Color(1f, 1f, 1f));
    }

    public void SetStarColorGold(Material mat)
    {
        mat.SetColor("_BaseColor", new Color(255 / 255f, 128 / 255f, 35 / 255f));
    }

    public void SetStarColorGray(Material mat)
    {
        mat.SetColor("_BaseColor", new Color(91 / 255f, 91 / 255f, 91 / 255f));
    }

    public void OnLateUpdate()
    {
        foreach (var pair in m_flashOnDic)
        {
            if (!pair.Value.target)
            {
                m_flashOnDic.Remove(pair.Key);
                break;
            }
            pair.Value.ratio += Time.deltaTime;
            var color = Color.Lerp(pair.Value.startColor, pair.Value.endColor, Mathf.Sin(pair.Value.ratio));
            m_outlineCtrl.ChangeColor(pair.Value.target, color);
        }
    }

    public void ShowBounds(GameObject target, float scaleUp = 1)
    {
        var bounds = CreateBoundFromMesh(target.gameObject);
        var cubeInstance = SourceAgent.Instance.LoadSource<GameObject>("CubeFrame", UFrame.Source.SourceLoadOption.MakeCopy);
        var size = bounds.size * scaleUp;
        size.x *= target.transform.lossyScale.x;
        size.y *= target.transform.lossyScale.y;
        size.z *= target.transform.lossyScale.z;
        var center = target.transform.TransformPoint(bounds.center);
        cubeInstance.transform.position = center;
        cubeInstance.transform.localScale = size;
        cubeInstance.transform.SetParent(target.transform.parent);
        cubeInstance.transform.localRotation = target.transform.localRotation;
    }

    public static Bounds CreateBoundFromMesh(GameObject target)
    {
        Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
        Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
        var meshFilters = target.GetComponentsInChildren<MeshFilter>();
        if (meshFilters.Length > 0)
        {
            for (int i = 0; i < meshFilters.Length; i++)
            {
                var childItem = meshFilters[i].transform;
                var vertices = meshFilters[i].sharedMesh.vertices;
                foreach (var vertice in vertices)
                {
                    var worldVerticePos = childItem.TransformPoint(vertice);
                    var localVerticePos = target.transform.InverseTransformPoint(worldVerticePos);
                    max.x = Mathf.Max(max.x, localVerticePos.x);
                    max.y = Mathf.Max(max.y, localVerticePos.y);
                    max.z = Mathf.Max(max.z, localVerticePos.z);

                    min.x = Mathf.Min(min.x, localVerticePos.x);
                    min.y = Mathf.Min(min.y, localVerticePos.y);
                    min.z = Mathf.Min(min.z, localVerticePos.z);
                }
            }
        }
        else
        {
            var meshRenders = target.GetComponentsInChildren<SkinnedMeshRenderer>();
            if (meshRenders.Length > 0)
            {
                var childPoints = target.GetComponentsInChildren<Transform>();
                for (int i = 0; i < childPoints.Length; i++)
                {
                    var childItem = childPoints[i].transform;
                    var localVerticePos = target.transform.InverseTransformPoint(childItem.position);

                    max.x = Mathf.Max(max.x, localVerticePos.x);
                    max.y = Mathf.Max(max.y, localVerticePos.y);
                    max.z = Mathf.Max(max.z, localVerticePos.z);

                    min.x = Mathf.Min(min.x, localVerticePos.x);
                    min.y = Mathf.Min(min.y, localVerticePos.y);
                    min.z = Mathf.Min(min.z, localVerticePos.z);
                }
                Debug.LogWarning("SkinnedMeshRenderer,use point get bound!" + target.name);
            }
            else
            {
                Debug.LogError("failed CreateBoundFromMesh!");
            }
        }
        return new Bounds((max + min) * 0.5f, max - min);
    }

    public GameObject MergeMeshCombineSmallDnyamicNoShadow(Transform parent,params GameObject[] others)
    {
        if (others == null && others.Length < 0)
            return null;
        var meshCombineStudioObj = SourceAgent.Instance.LoadSource<GameObject>("MeshCombineStudio", UFrame.Source.SourceLoadOption.MakeCopy);
        meshCombineStudioObj.transform.SetParent(parent);
        var combiner = meshCombineStudioObj.GetComponent<MeshCombineStudio.MeshCombiner>();
        combiner.combineMode = MeshCombineStudio.CombineMode.DynamicObjects;
        combiner.searchOptions.parentGOs = others;
        combiner.combineConditionSettings.sameReceiveGI = false;
        combiner.combineConditionSettings.sameReceiveShadows = false;
        combiner.combineConditionSettings.sameShadowCastingMode = false;
        combiner.cellOffset = new Vector3(2, 2, 2);
        combiner.cellSize = 4;
        combiner.searchOptions.onlyStatic = false;
        combiner.gameObject.SetActive(true);
        return meshCombineStudioObj;
    }
}